﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using Infinity.Expressions.Commands;

namespace Infinity.Expressions.Core
{			   
	/// <summary>
	/// Főprogramot reprezentáló osztály
	/// </summary>
	public class MainProgram : ProcedureCommand
	{
		/// <summary>
		/// Az állapottér változói
		/// </summary>
		public VariableCollection Variables { get; set; }
		/// <summary>
		/// Az állapottér paramétertere
		/// </summary>
		public VariableCollection Parameters { get; set; }

		/// <summary>
		/// A program neve
		/// </summary>
		public string Name { get; set; }

		public MainProgram()
		{
			Variables = new VariableCollection();
			Parameters = new VariableCollection();
		}															  

		public override void Run()
		{
			var expression = this.ToExpression();
			var resultList = Expression.Lambda<Func<object>>(expression).Compile()() as IList<object>;
			var varList = Variables.ToArray();
			for (var i = 0; i < varList.Length; i++)
			{
				varList[i].Value.Value = resultList[i];
			}
		}

		public override Expression ToExpression()
		{
			var variableList = Variables.Select((obj) => obj.Value.ToExpression()).ToArray();
			//LabelTarget returnLabel = Expression.Label(typeof(object[]), "return");

			var blockBody = //az előfeltétel ellenőrzés beiktatása
							new[] { PreValidate() }
							.Union(
							//az állapottér változóinak értékadása
							from var in Variables
							let variable = var.Value
							select (Expression)Expression.Assign(variable.ToExpression(), Expression.Constant(variable.Value)))
							.Union(
							//a "hasznos" program
							from i in new[] { Body }
							where i != null
							select Body.ToExpression())
							//az utófeltétel ellenőrzés beiktatása
							.Union( new[] { PostValidate() } )
							.Union(
								//az állapottér változói értékének visszaadása
								new[] { Expression.NewArrayInit(typeof(object), variableList.Select((x) => Expression.Convert(x, typeof(object)))) }
							);

			var block = Expression.Block(variableList, blockBody);

			return block;
		}
	}
}
